package com.sky.service.user.impl;

import com.alibaba.fastjson.JSON;
import com.sky.constant.MessageConstant;
import com.sky.dto.NotifyDto;
import com.sky.dto.PayDto;
import com.sky.exception.DataException;
import com.sky.jsapi.dto.Amount;
import com.sky.jsapi.dto.JsapiPayDto;
import com.sky.jsapi.dto.Payer;
import com.sky.mapper.common.OrderMapper;
import com.sky.mapper.common.UserMapper;
import com.sky.natives.dto.NativePayDto;
import com.sky.natives.dto.RefundAmount;
import com.sky.natives.dto.RefundDto;
import com.sky.pojo.Orders;
import com.sky.pojo.User;
import com.sky.properties.WeChatProperties;
import com.sky.result.Result;
import com.sky.service.user.WxPayService;
import com.sky.websocket.WebSocketServer;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.Signature;
import java.time.LocalDateTime;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Service
public class WxPayServiceImpl  implements WxPayService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private CloseableHttpClient httpClient;

    @Autowired
    private WeChatProperties weChatProperties;

    @Autowired
    private WebSocketServer webSocketServer;

    /**
     * 完成小程序的下单
     *
     * @param dto
     * @return
     */
    @Override
    public Result<String> payment(PayDto dto) throws Exception {
        //1. 校验参数
        if(null == dto || null == dto.getOrderNumber() || "".equals(dto.getOrderNumber()))
        {
            throw new DataException(MessageConstant.DATA_ERROR);
        }

        //2. 查询订单 如果订单不存在则抛出异常
        Orders orders = orderMapper.selectByNumber(dto.getOrderNumber());
        if(null == orders){
            throw new DataException(MessageConstant.DATA_ERROR);
        }

        //调用二维码支付下单
        nativeCreateOrder(orders);

       /* //3. 从threadLocal中获取用户的id 然后查询user表得到openid
        Long userId = BaseContext.getCurrentId();
        User user = userMapper.selectById(userId);

        //4. 调用微信的api完成下单的功能
        String prepayId = jsapiCreateOrder(orders, user);

        //5. 调用微信的工具类完成预支付订单的签名
        String sigin = jsApiCreateSign(prepayId);*/

        //6. 返回签名的加密字符串给小程序
        return Result.success();
    }

    /**
     * 二维码支付通知回调
     *
     * @param notifyDto
     */
    @Override
    public void handleNotify(NotifyDto notifyDto) {
        AesUtil aesUtil = new AesUtil(weChatProperties.getApiV3Key().getBytes(StandardCharsets.UTF_8));
        try {
            String json = aesUtil.decryptToString(notifyDto.getResource().getAssociated_data().getBytes(StandardCharsets.UTF_8),
                    notifyDto.getResource().getNonce().getBytes(StandardCharsets.UTF_8),
                    notifyDto.getResource().getCiphertext()
            );

            Map map = JSON.parseObject(json, Map.class);
            String  outTradeNo = (String) map.get("out_trade_no");
            if(null != outTradeNo){
                Orders orders = new Orders();
                orders.setPayStatus(Orders.PAID);
                orders.setStatus(Orders.TO_BE_CONFIRMED);
                orders.setCheckoutTime(LocalDateTime.now());
                orders.setNumber(outTradeNo);

                orderMapper.updateByOutTradeNo(orders);

                //发送消息通知商家有用户下单了
                map = new HashMap<>();
                map.put("type", "1");
                map.put("orderId", orders.getId());
                map.put("content", "客户下单了 请及时处理");

                webSocketServer.sendToAllClient(JSON.toJSONString(map));
            }

        } catch (GeneralSecurityException e) {
            throw new RuntimeException(e);
        }
    }

    public void nativeCreateOrder(Orders orders) throws Exception{
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/native");
        // 请求body参数
        Amount amount = Amount.builder()
                .total(1)
                .currency("CNY")
                .build();
        NativePayDto nativePayDto = NativePayDto.builder()
                .amount(amount)
                .appid("wxffb3637a228223b8")
                .description("苍穹外卖")
                .mchid(weChatProperties.getMchid())
                .out_trade_no(orders.getNumber())
                .notify_url(weChatProperties.getNativeNotifyUrl())
                .build();
        String reqdata = JSON.toJSONString(nativePayDto);
        StringEntity entity = new StringEntity(reqdata,"utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");

        //完成签名并执行请求
        CloseableHttpResponse response = httpClient.execute(httpPost);

        try {
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) { //处理成功
                System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
            } else if (statusCode == 204) { //处理成功，无返回Body
                System.out.println("success");
            } else {
                System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
                throw new IOException("request failed");
            }
        } finally {
            response.close();
        }
    }

    /**
     * 生成签名的方法
     * @param prepayId
     * @return
     */
    private String jsApiCreateSign(String prepayId) throws Exception {
        String message = buildMessage(
                weChatProperties.getAppid(),
                System.currentTimeMillis(),
                UUID.randomUUID().toString().replaceAll("-", ""),
                "prepay_id=" + prepayId
        );

        return sign(message.getBytes(StandardCharsets.UTF_8));
    }

    String sign(byte[] message) throws Exception {
        Signature sign = Signature.getInstance("SHA256withRSA");
        PrivateKey merchantPrivateKey = PemUtil
                .loadPrivateKey(new FileInputStream(new File(weChatProperties.getPrivateKeyFilePath())));
        sign.initSign(merchantPrivateKey);
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }

    String buildMessage(String appid, long timestamp, String nonceStr, String packageStr) {
        return appid + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + packageStr + "\n";
    }

    private String jsapiCreateOrder(Orders orders, User user) throws Exception {
        //请求URL
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");

        // 请求body参数
        Amount amount = Amount.builder()
//                .total(orders.getAmount().intValue())
                .total(1)
                .currency("CNY")
                .build();
        Payer payer = Payer.builder()
                .openid(user.getOpenid())
                .build();

        JsapiPayDto jsapiPayDto = JsapiPayDto.builder()
                .mchid(weChatProperties.getMchid())
                .appid(weChatProperties.getAppid())
                .notify_url(weChatProperties.getNotifyUrl())
                .out_trade_no(orders.getNumber())
                .amount(amount)
                .payer(payer)
                .description("黑马架构师课程v1.0")
                .build();

        String reqdata = JSON.toJSONString(jsapiPayDto);
        StringEntity entity = new StringEntity(reqdata,"utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");

        //完成签名并执行请求
        CloseableHttpResponse response = httpClient.execute(httpPost);
        try {
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                //{"prepay_id": "wx26112221580621e9b071c00d9e093b0000"}
                String json = EntityUtils.toString(response.getEntity());
                Map map = JSON.parseObject(json, Map.class);
                return map.get("prepay_id").toString();
            } else if (statusCode == 204) {
                System.out.println("success");

                return "";
            } else {
                System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
                throw new IOException("request failed");
            }
        } finally {
            response.close();
        }
    }

    public Boolean QueryOrder(Orders orders) throws Exception {

        //请求URL
        URIBuilder uriBuilder = new URIBuilder("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + orders.getNumber());
        uriBuilder.setParameter("mchid", weChatProperties.getMchid());

        //完成签名并执行请求
        HttpGet httpGet = new HttpGet(uriBuilder.build());
        httpGet.addHeader("Accept", "application/json");
        CloseableHttpResponse response = httpClient.execute(httpGet);

        try {
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
                String json = EntityUtils.toString(response.getEntity());
                Map map = JSON.parseObject(json, Map.class);
                return "SUCCESS".equals(map.get("trade_state"));

            } else if (statusCode == 204) {
                System.out.println("success");
                return Boolean.FALSE;
            } else {
                System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
                //throw new IOException("request failed");
                return Boolean.FALSE;
            }
        } finally {
            response.close();
        }
    }

    public void CloseOrder(String orderNum) throws Exception {

        //请求URL
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + orderNum + "/close");
        //请求body参数
        String reqdata ="{\"mchid\": \""+weChatProperties.getMchid()+"\"}";

        StringEntity entity = new StringEntity(reqdata,"utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");

        //完成签名并执行请求
        CloseableHttpResponse response = httpClient.execute(httpPost);
        try {
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
            } else if (statusCode == 204) {
                System.out.println("关闭订单成功");
            } else {
                System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
                throw new IOException("request failed");
            }
        } finally {
            response.close();
        }
    }

    /**
     * 订单退款接口
     *
     * @param orders
     */
    @Override
    public void refunds(Orders orders, String cancelReson) throws IOException {
        //请求URL
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds");
        //请求body参数
        RefundAmount refundAmount =RefundAmount.builder()
                .refund(1)
                .currency("CNY")
                .total(1)
                .build();
        RefundDto refundDto = RefundDto.builder()
                .out_refund_no(UUID.randomUUID().toString().replaceAll("-", ""))
                .out_trade_no(orders.getNumber())
                .amount(refundAmount)
                .build();

        String reqdata = JSON.toJSONString(refundDto);

        StringEntity entity = new StringEntity(reqdata,"utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");

        //完成签名并执行请求
        CloseableHttpResponse response = httpClient.execute(httpPost);
        try {
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
                String json = EntityUtils.toString(response.getEntity());
                Map map = JSON.parseObject(json, Map.class);
                if("PROCESSING".equals(map.get("status"))){
                    //2. 修改订单的状态
                    orders.setCancelReason(cancelReson);
                    orders.setStatus(Orders.CANCELLED_ORDER);
                    orders.setCancelTime(LocalDateTime.now());
                    orderMapper.updateById(orders);
                }
            }
        } finally {
            response.close();
        }
    }
}
